import RequestResult = wx.RequestResult;


type HTTPHeader = { [key: string]: string }

export function createXHRClass(wx) {

  return class WXXHR implements XMLHttpRequest {
    readonly DONE: number;
    readonly HEADERS_RECEIVED: number;
    readonly LOADING: number;
    readonly OPENED: number;
    readonly UNSENT: number;
    onabort: ((this: XMLHttpRequest, ev: ProgressEvent) => any) | null;
    onerror: ((this: XMLHttpRequest, ev: ProgressEvent) => any) | null;
    onload: ((this: XMLHttpRequest, ev: ProgressEvent) => any) | null;
    onloadend: ((this: XMLHttpRequest, ev: ProgressEvent) => any) | null;
    onloadstart: ((this: XMLHttpRequest, ev: ProgressEvent) => any) | null;
    onprogress: ((this: XMLHttpRequest, ev: ProgressEvent) => any) | null;
    onreadystatechange: ((this: XMLHttpRequest, ev: Event) => any) | null;
    ontimeout: ((this: XMLHttpRequest, ev: ProgressEvent) => any) | null;

    readyState: number;

    response: any;
    responseText: string;
    responseType: XMLHttpRequestResponseType;
    readonly responseURL: string;
    readonly responseXML: Document | null;
    status: number;
    statusText: string;
    timeout: number;
    readonly upload: XMLHttpRequestUpload = null;
    withCredentials: boolean;

    method: string;
    url: string;
    header: HTTPHeader = {}
    resHeaders: HTTPHeader = {};


    abort(): void {
    }

    addEventListener<K extends keyof XMLHttpRequestEventMap>(type: K, listener: (this: XMLHttpRequest, ev: XMLHttpRequestEventMap[K]) => any, options?: boolean | AddEventListenerOptions): void;
    addEventListener(type: string, listener: EventListenerOrEventListenerObject, options?: boolean | AddEventListenerOptions): void;
    addEventListener<K extends keyof XMLHttpRequestEventTargetEventMap>(type: K, listener: (this: XMLHttpRequestEventTarget, ev: XMLHttpRequestEventTargetEventMap[K]) => any, options?: boolean | AddEventListenerOptions): void;
    addEventListener(type: string, listener: EventListenerOrEventListenerObject | null, options?: boolean | AddEventListenerOptions): void;
    addEventListener(type, listener, options?: boolean | AddEventListenerOptions): void {
    }

    dispatchEvent(event: Event): boolean {
      return false;
    }

    getAllResponseHeaders(): string {

      return Object.keys(this.resHeaders).map(key =>
        `${key}:${this.resHeaders[key]}`
      ).join('\n')

    }

    getResponseHeader(name: string): string | null {
      return this.resHeaders[name] || null
    }

    open(method: string, url: string): void;
    open(method: string, url: string, async: boolean, username?: string | null, password?: string | null): void;
    open(method: string, url: string, async?: boolean, username?: string | null, password?: string | null): void {
      this.method = method
      this.url = url
    }

    overrideMimeType(mime: string): void {
    }

    removeEventListener<K extends keyof XMLHttpRequestEventMap>(type: K, listener: (this: XMLHttpRequest, ev: XMLHttpRequestEventMap[K]) => any, options?: boolean | EventListenerOptions): void;
    removeEventListener(type: string, listener: EventListenerOrEventListenerObject, options?: boolean | EventListenerOptions): void;
    removeEventListener<K extends keyof XMLHttpRequestEventTargetEventMap>(type: K, listener: (this: XMLHttpRequestEventTarget, ev: XMLHttpRequestEventTargetEventMap[K]) => any, options?: boolean | EventListenerOptions): void;
    removeEventListener(type: string, callback: EventListenerOrEventListenerObject | null, options?: EventListenerOptions | boolean): void;
    removeEventListener(type, listener, options?: boolean | EventListenerOptions): void {
    }

    send(body?: Document | BodyInit | null): void {
      this.status = 3
      this.statusText = '3 LOADING'
      wx.request({
          method: this.method,
          url: this.url,
          data: body,
          header: this.header,
          success: (res: RequestResult) => {

            this.readyState = 4
            this.status = res.statusCode
            this.statusText = '4 DONE'

            const resHeaders = res.header || {}

            this.resHeaders = Object.keys(res.header || {})
              .reduce((headers, key) => {
                headers[key.toLocaleLowerCase()] = resHeaders[key]
                return headers
              }, {})

            this.response = res.data
            if (typeof res.data === 'string') {
              this.responseText = res.data
            }

            if (res.data.toString() === '[object Object]') {
              this.responseType = ''
              this.responseText = JSON.stringify(res.data)
            }

            this.onreadystatechange && this.onreadystatechange(null)
          },
          fail: () => {
            this.readyState = 4
            this.status = 400
            this.responseText = ''
            this.responseType = ''

            this.onreadystatechange && this.onreadystatechange(null)
          }
        }
      )
    }

    setRequestHeader(name: string, value: string): void {
      this.header[name] = value
    }
  }

}
